<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>F1 起步反应测试 · 触控版（按住屏幕=离合）</title>
<style>
  :root { --bg:#f6f7fb; --text:#111; --muted:#666; --panel:#222; --dim:#5a0a0a; --red:#e62121; --off:#999; --ok:#1a8f3a; --warn:#c73c00; }
  *{box-sizing:border-box} html,body{height:100%}
  body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;background:var(--bg);color:var(--text);display:grid;place-items:center}
  .wrap{width:min(960px,94vw);padding:16px}
  h1{margin:0 0 8px;font-size:clamp(18px,2.4vw,26px)}
  .lights{margin:10px auto;background:var(--panel);border-radius:20px;padding:clamp(10px,2vw,18px);width:min(720px,96%)}
  .grid{display:grid;grid-template-columns:repeat(5,1fr);gap:clamp(8px,2vw,20px);min-height:clamp(160px,30vw,240px)}
  .cell{display:grid;grid-template-rows:1fr 1fr;gap:clamp(8px,2vw,20px)}
  .led{width:100%;aspect-ratio:1/1;border-radius:50%;background:#300;box-shadow:inset 0 0 18px rgba(0,0,0,.6);transition:background .08s linear,filter .08s linear;filter:brightness(.9)}
  .led.on{background:var(--red);filter:brightness(1)}
  .led.dim{background:var(--dim)} .led.off{background:var(--off)}
  .status{text-align:center;margin:8px 0 0;font-size:clamp(15px,2.2vw,18px)}
  .row{display:flex;gap:12px;align-items:baseline;justify-content:center;flex-wrap:wrap;margin-top:8px}
  .big{font-size:clamp(30px,7vw,60px);font-variant-numeric:tabular-nums}
  .tag{padding:2px 8px;border-radius:999px;background:#eee;color:#333;font-size:13px}
  .tag.ok{background:#e7f6eb;color:var(--ok)} .tag.err{background:#fff0e6;color:var(--warn)}
  .area{margin-top:10px;display:grid;place-items:center}
  .hold{user-select:none;-webkit-user-select:none;touch-action:none;width:min(720px,96vw);height:22vh;min-height:140px;border-radius:16px;background:white;box-shadow:0 2px 12px rgba(0,0,0,.08);display:grid;place-items:center}
  .hold:active{transform:translateY(1px)}
  .hint{color:var(--muted);text-align:center;margin-top:6px;font-size:12px}
  footer{text-align:center;color:var(--muted);margin-top:8px;font-size:12px}
  button{border:none;background:#fff;border-radius:10px;padding:8px 12px;box-shadow:0 2px 8px rgba(0,0,0,.08);cursor:pointer;font-size:14px}
</style>
</head>
<body>
<div class="wrap">
  <h1>F1 起步反应测试 · 触控版（按住屏幕=捏离合）</h1>
  <div class="lights">
    <div class="grid" id="grid"></div>
    <div class="status" id="status">按住下方白色区域进入待发</div>
  </div>

  <div class="row">
    <div class="big" id="time">0.000 s</div>
    <span class="tag" id="label">等待中</span>
    <span class="tag">最佳：<span id="best">—</span></span>
    <button id="restart">重置</button>
  </div>

  <div class="area">
    <div class="hold" id="hold"><div>按住</div></div>
    <div class="hint">按住=捏离合；灯灭瞬间松开=起步。长按白色区即可完成全流程（无需键盘）。</div>
  </div>

  <footer>Made for iPhone/iPad · 离线可用</footer>
</div>

<script>
(() => {
  const LIGHT_ON_INTERVAL = 550;
  const FULL_ON_RANDOM = [1200, 3000];
  const JUMP_GRACE = 40;

  const grid = document.getElementById('grid');
  const statusEl = document.getElementById('status');
  const timeEl = document.getElementById('time');
  const labelEl = document.getElementById('label');
  const bestEl = document.getElementById('best');
  const restartBtn = document.getElementById('restart');
  const hold = document.getElementById('hold');

  const cols = 5; const lights = [];
  for (let c=0;c<cols;c++){const col=document.createElement('div');col.className='cell';
    const top=document.createElement('div');const bot=document.createElement('div');
    top.className='led dim'; bot.className='led dim'; col.append(top,bot); grid.append(col); lights.push([top,bot]);}

  const S={WAIT:0,FILL:1,FULL:2,GO:3,RESULT:4}; let state=S.WAIT;
  let lightsOn=0, timers=[], goAt=0, best=parseFloat(localStorage.getItem('f1_best_touch')||'NaN');
  if(!isNaN(best)) bestEl.textContent=best.toFixed(3)+' s';

  const setStatus=t=>statusEl.textContent=t;
  const setLabel=(t,k='')=>{labelEl.textContent=t; labelEl.className='tag '+k;}
  const setTime=v=>timeEl.textContent=v.toFixed(3)+' s';
  const clearTimers=()=>{timers.forEach(clearTimeout); timers=[];}
  const setAllDim=()=>lights.forEach(([a,b])=>[a,b].forEach(el=>el.className='led dim'));
  const setAllOff=()=>lights.forEach(([a,b])=>[a,b].forEach(el=>el.className='led off'));
  const setColsOn=n=>{for(let i=0;i<cols;i++){const on=i<n;[lights[i][0],lights[i][1]].forEach(el=>el.className='led '+(on?'on':'dim'));}};

  function reset(msg='按住下方白色区域进入待发'){clearTimers();state=S.WAIT;lightsOn=0;setAllDim();goAt=0;setTime(0);setLabel('等待中');setStatus(msg);}
  function startFill(){state=S.FILL;lightsOn=0;setLabel('准备中');setStatus('保持按住…');setAllDim();timers.push(setTimeout(stepFill,350));}
  function stepFill(){if(state!==S.FILL)return;lightsOn++;setColsOn(lightsOn);
    if(lightsOn>=cols){state=S.FULL;setLabel('保持');setStatus('五灯全亮… 不要松！');
      const wait=FULL_ON_RANDOM[0]+Math.random()*(FULL_ON_RANDOM[1]-FULL_ON_RANDOM[0]);
      goAt=performance.now()+wait; timers.push(setTimeout(lightsOut,wait));}
    else{timers.push(setTimeout(stepFill,LIGHT_ON_INTERVAL));}}
  function lightsOut(){if(state!==S.FULL&&state!==S.FILL)return;state=S.GO;setAllOff();goAt=performance.now();setLabel('GO!','ok');setStatus('松开！');}
  function release(now){if(state===S.FILL||state===S.FULL){const msToGo=goAt?goAt-now:Infinity; if(msToGo>-JUMP_GRACE) showResult(null,true);}
                        else if(state===S.GO){showResult((now-goAt)/1000,false);}}
  function showResult(sec,jumped){state=S.RESULT;
    if(jumped){setLabel('抢跑','err');setStatus('灯灭前松开了。点“重置”重来。');}
    else{setLabel('完成','ok');setStatus('点“重置”重来。');setTime(sec);
      if(isFinite(sec)&&(isNaN(best)||sec<best)){best=sec;localStorage.setItem('f1_best_touch',String(best));bestEl.textContent=best.toFixed(3)+' s';}}}

  let holding=false;
  const prevent = e => { e.preventDefault(); e.stopPropagation(); };
  const down = e => { if(!holding){holding=true;if(state===S.WAIT) startFill();} prevent(e); };
  const up = e => { if(holding){holding=false;release(performance.now());} prevent(e); };

  ['touchstart','mousedown','pointerdown'].forEach(ev=>hold.addEventListener(ev,down,{passive:false}));
  ['touchend','mouseup','mouseleave','pointerup','pointercancel'].forEach(ev=>hold.addEventListener(ev,up,{passive:false}));
  restartBtn.addEventListener('click',()=>reset());

  reset();
})();
</script>
</body>
</html>
